package org.mysterylab.utopiaframework.core.orm;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.mysterylab.utopiaframework.util.reflect.ConvertUtil;
import org.mysterylab.utopiaframework.util.web.HttpUtil;
import org.springframework.util.Assert;

/**
 * 与具体的 ORM 实现无关的属性过滤条件封装类, 主要记录页面中简单的搜索过滤条件.
 * 
 * @author zhouych
 */
public class PropertyFilter {

	/**
	 * 多个属性间 OR 关系的分隔符
	 */
	public static final String OR_SEPARATOR = "_OR_";

	/**
	 * 基本属性比较类型
	 */
	public enum MatchType {
		EQ, LIKE, LT, GT, LE, GE;
	}
	
	/**
	 * 基本属性数据类型
	 */
	public enum PropertyType {
		
		B(Boolean.class),	// Boolean 类型
		I(Integer.class),	// Integer 类型
		N(Double.class),	// Double 类型
		L(Long.class),		// Long 类型
		S(String.class),	// String 类型
		D(Date.class);		// Date 类型

		private Class<?> clazz;

		private PropertyType(Class<?> clazz) {
			this.clazz = clazz;
		}

		public Class<?> getValue() {
			return clazz;
		}
	}
	
	/**
	 * 比较类型
	 */
	private MatchType matchType;
	
	/**
	 * 比较值
	 */
	private Object matchValue;
	
	/**
	 * 属性过滤类
	 */
	private Class<?> propertyClass;
	
	/**
	 * 比较属性过滤名列表
	 */
	private String[] propertyNames;
	
	public PropertyFilter() {
		
	}

	/**
	 * PropertyFilter 构造函数.
	 * 
	 * @param filterName
	 *            比较属性字符串, 含待比较的比较类型、属性值类型及属性列表. 比如: LIKES_NAME_OR_LOGIN_NAME
	 * @param value
	 *            待比较的值, 比如如果是 Boolean 类型可以为 "true", Date 类型可以为 "2011-07-28"
	 */
	public PropertyFilter(final String filterName, final String value) {
		
		/*
		 * 假设当前的 filterName 是 LIKES_NAME_OR_LOGIN_NAME, 则:
		 * 1) firstPart			= LIKES
		 * 2) matchTypeCode		= LIKE
		 * 3) propertyTypeCode	= S
		 * 4) propertyNameStr	= NAME_OR_LOGIN_NAME
		 * 5) propertyNames[]	= [NAME, LOGIN_NAME]
		 */
		String firstPart = StringUtils.substringBefore(filterName, "_");
		String matchTypeCode = StringUtils.substring(firstPart, 0, firstPart.length() - 1);
		String propertyTypeCode = StringUtils.substring(firstPart, firstPart.length() - 1,
				firstPart.length());
		
		try {
			matchType = Enum.valueOf(MatchType.class, matchTypeCode);
		} catch (RuntimeException e) {
			throw new IllegalArgumentException("fileter - " + filterName
					+ " does not write as rule, so can not get the property match type."
					+ e);
		}
		try {
			propertyClass = Enum.valueOf(PropertyType.class, propertyTypeCode).getValue();
		} catch (RuntimeException e) {
			throw new IllegalArgumentException("fileter-" + filterName
					+ " does not write as rule, so can not get the property value type."
					+ e);
		}
		
		String propertyNameStr = StringUtils.substringAfter(filterName, "_");
		Assert.isTrue(StringUtils.isNotBlank(propertyNameStr), "filter - " + filterName
				+ " does not write as rule, so can not get the property name.");
		propertyNames = StringUtils.splitByWholeSeparator(propertyNameStr,
				PropertyFilter.OR_SEPARATOR);
		this.matchValue = ConvertUtil.convertStringToObject(value, propertyClass);
	}

	/**
	 * 从 HttpRequest 中创建 PropertyFilter 列表, 默认 Filter 属性名前缀为 filter.
	 * 
	 * @param request
	 *            客户端请求
	 * @return
	 * @see #buildFromHttpRequest(HttpServletRequest, String)
	 */
	public static List<PropertyFilter> buildFromHttpRequest(
			final HttpServletRequest request) {
		return buildFromHttpRequest(request, "filter");
	}

	/**
	 * 从 HttpRequest 中创建 PropertyFilter 列表, PropertyFilter 命名规则为
	 * Filter 属性前缀_比较类型属性类型_属性名.
	 * 比如: filter_EQS_name & filter_LIKES_name_OR_email
	 * 
	 * @param request
	 *            客户端请求
	 * @param filterPrefix
	 *            过滤器的前缀名
	 * @return
	 */
	public static List<PropertyFilter> buildFromHttpRequest(
			final HttpServletRequest request, final String filterPrefix) {
		
		List<PropertyFilter> filterList = new ArrayList<PropertyFilter>();

		// 从 request 中获取含属性前缀名的参数, 构造去除前缀名后的参数 Map
		Map<String, Object> filterParamMap = HttpUtil.getParametersStartsWith(
				request, filterPrefix + "_");

		// 分析参数 Map, 构造 PropertyFilter 列表
		for (Map.Entry<String, Object> entry : filterParamMap.entrySet()) {
			String filterName = entry.getKey();
			String value = (String) entry.getValue();
			// 如果 value 值为空, 则忽略此 filter.
			if (StringUtils.isNotBlank(value)) {
				PropertyFilter filter = new PropertyFilter(filterName, value);
				filterList.add(filter);
			}
		}
		
		return filterList;
	}

	public MatchType getMatchType() {
		return matchType;
	}

	public Object getMatchValue() {
		return matchValue;
	}

	public Class<?> getPropertyClass() {
		return propertyClass;
	}

	public String[] getPropertyNames() {
		return propertyNames;
	}
	
	/**
	 * 获取唯一的比较属性名称.
	 * 
	 * @return
	 */
	public String getPropertyName() {
		Assert.isTrue(propertyNames.length == 1,
				"there are not only one property in this filter");
		return propertyNames[0];
	}
	
	/**
	 * 是否比较多个属性.
	 * 
	 * @return
	 */
	public boolean hasMultiProperties() {
		return (propertyNames.length > 1);
	}
}
